package guitplugin.guitview;

import com.sun.mirror.declaration.AnnotationMirror;
import com.sun.mirror.declaration.AnnotationTypeElementDeclaration;
import com.sun.mirror.declaration.AnnotationValue;
import com.sun.mirror.declaration.ClassDeclaration;

import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 * Simplifies instantiation of the w3c XML parser, in just the style that
 * UiBinder likes it. Used by both prod and test.
 */
public class W3cDomHelper {
  private final DocumentBuilderFactory factory;
  private final DocumentBuilder builder;

  public W3cDomHelper() {
    factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    factory.setExpandEntityReferences(true);
    try {
      builder = factory.newDocumentBuilder();
    } catch (ParserConfigurationException e) {
      throw new RuntimeException(e);
    }
    builder.setEntityResolver(new GwtResourceEntityResolver());
    builder.setErrorHandler(new ErrorHandler() {

      @Override
      public void error(SAXParseException exception) throws SAXException {
        exception.printStackTrace();
      }

      @Override
      public void fatalError(SAXParseException exception) throws SAXException {
        exception.printStackTrace();
      }

      @Override
      public void warning(SAXParseException exception) throws SAXException {
        exception.printStackTrace();
      }
    });
  }

  public Document documentFor(String[] root, ClassDeclaration classDeclaration)
      throws SAXParseException {
    String viewName = classDeclaration.getSimpleName() + ".ui.xml";
    Collection<AnnotationMirror> mirrors = classDeclaration.getAnnotationMirrors();
    for (AnnotationMirror mirror : mirrors) {
      if (mirror.getAnnotationType().getDeclaration().getQualifiedName().equals(
          "com.guit.client.ViewTemplate")) {
        Map<AnnotationTypeElementDeclaration, AnnotationValue> values = mirror.getElementValues();
        for (Entry<AnnotationTypeElementDeclaration, AnnotationValue> entry : values.entrySet()) {
          viewName = (String) entry.getValue().getValue();
          break;
        }
        break;
      }
    }

    String basePackage = classDeclaration.getPackage().getQualifiedName().replace(".", "/") + "/";

    // Try to locate in the same package
    FileInputStream fis = null;
    try {
      fis = locateFile(root, basePackage + viewName);

      if (fis == null) {
        fis = locateFile(root, basePackage + "view/" + viewName);
      }

      if (fis == null) {
        throw new RuntimeException("The view '" + classDeclaration.getQualifiedName()
            + ".ui.xml' was not found");
      }

      return builder.parse(fis);
    } catch (SAXParseException e) {
      throw e;
    } catch (SAXException e) {
      throw new RuntimeException(e);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public FileInputStream locateFile(String[] root, String file) {
    FileInputStream fis = null;
    for (int n = 0; n < root.length && fis == null; n++) {
      String r = root[n];

      // Windows
      if (isWindows()) {
        int index = r.indexOf(";");
        if (index != -1) {
          r = r.substring(index + 1, r.length()) + ":\\" + r.substring(0, index);
        }
      }

      File f = new File(r + "/" + file);
      if (f.exists()) {
        try {
          fis = new FileInputStream(f);
        } catch (FileNotFoundException e) {
          continue;
        }
        break;
      }
    }
    return fis;
  }

  public static boolean isWindows() {
    String os = System.getProperty("os.name").toLowerCase();
    return (os.indexOf("win") >= 0);
  }
}
